Kotlin-Mvvm-DaggerHilt-Room-Preferences Data Store-Login SignUp Logout-Example
Step 1:
Open build.gradle(:Project) and add following dependency and rebuild the project.
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.4"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20"
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.40.5'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Step 2:
Open build.gradle(:app) and add following dependency and rebuild the project.
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
compileSdk 31
defaultConfig {
applicationId "in.kotlinkatta.kotlinkattademo"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures{
dataBinding true;
viewBinding true
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
//kotlin Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
//room
implementation "androidx.room:room-ktx:2.4.1"
implementation "androidx.room:room-runtime:2.4.1"
kapt "androidx.room:room-compiler:2.4.1"
//multidex
implementation 'androidx.multidex:multidex:2.0.1'
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0"
// view model ktx
implementation 'androidx.activity:activity-ktx:1.4.0'
//hilt viewmodel
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'
kapt("org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.3.0")
//retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
//moshi
implementation("com.squareup.moshi:moshi-kotlin:1.12.0")
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
//SharedPreferences
implementation("androidx.datastore:datastore:1.0.0")
implementation("androidx.datastore:datastore-preferences:1.0.0")
//DaggerHilt
implementation 'com.google.dagger:hilt-android:2.40.5'
kapt 'com.google.dagger:hilt-compiler:2.40.5'
}
// Allow references to generated code
kapt {
correctErrorTypes = true
}
Step 3:
Open gradle.properties and add following code and rebuild the project.
kapt.use.worker.api=false
Step 4:
Open AndroidManifest.xml and add following dependency and rebuild the project.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="in.kotlinkatta.kotlinkattademo">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:name=".container.BaseApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.KotlinKattaDemo">
<activity
android:name=".LoginActivity"
android:exported="false" />
<activity
android:name=".SignUpActivity"
android:exported="false" />
<activity
android:name=".WelcomeActivity"
android:exported="false" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
App Flow Image:
Step 5:
Create new class BaseApp.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.container
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class BaseApp : Application() {}
Step 6:
Create new class UserDataStore.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.datastore
import android.content.Context
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
import java.io.IOException
import javax.inject.Inject
class UserDataStore @Inject constructor(@ApplicationContext val context: Context) {
companion object {
private val Context.dataStore by preferencesDataStore(name = "kotlinkatta")
}
object PreferencesKey {
val userloginstatus = stringPreferencesKey("userloginstatus")
}
suspend fun saveToLocal_userloginstatus(userloginstatus: String) {
context.dataStore.edit { preference ->
preference[PreferencesKey.userloginstatus] = userloginstatus
}
}
val readFromLocal_userloginstatus: Flow<String> = context.dataStore.data
.catch { exception ->
if (exception is IOException) {
emit(emptyPreferences())
} else {
throw exception
}
}.map { preference ->
val userloginstatus = preference[PreferencesKey.userloginstatus] ?: "0"
userloginstatus
}
}
Step 7:
Create new data class UserModel.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.model
import androidx.annotation.Keep
import androidx.room.Entity
import androidx.room.PrimaryKey
@Keep
@Entity(tableName = "usertable")
data class UserModel(
val useremail: String,
val userpassword: String
) {
@PrimaryKey(autoGenerate = true)
var userid: Int = 0
}
Step 8:
Create new interface UserDao.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.dao
import `in`.kotlinkatta.kotlinkattademo.model.UserModel
import androidx.room.*
import kotlinx.coroutines.flow.Flow
@Dao
interface UserDao {
//Insert
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUserData(noteModel: UserModel)
@Query("SELECT COUNT(*) FROM usertable WHERE useremail LIKE :useremail and userpassword LIKE :userpassword")
fun checkUserLoginDetails(useremail: String, userpassword: String): Flow<Int>
}
Step 9:
Create new abstract class UserDatabase.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.database
import `in`.kotlinkatta.kotlinkattademo.dao.UserDao
import `in`.kotlinkatta.kotlinkattademo.model.UserModel
import androidx.room.Database
import androidx.room.RoomDatabase
@Database(entities = [UserModel::class], version = 1,exportSchema = false)
abstract class UserDatabase : RoomDatabase() {
abstract fun getUserDao(): UserDao
}
Step 10:
Create new object AppModule.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.di
import `in`.kotlinkatta.kotlinkattademo.dao.UserDao
import `in`.kotlinkatta.kotlinkattademo.database.UserDatabase
import android.content.Context
import androidx.room.Room
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun providesDatabase(@ApplicationContext context: Context): UserDatabase =
Room.databaseBuilder(context, UserDatabase::class.java, "UserDatabase")
.build()
@Provides
fun providesUserDao(userDatabase: UserDatabase): UserDao =
userDatabase.getUserDao()
}
Step 11:
Create new class UserRepository.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.repository
import `in`.kotlinkatta.kotlinkattademo.dao.UserDao
import `in`.kotlinkatta.kotlinkattademo.model.UserModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
import javax.inject.Inject
class UserRepository @Inject constructor(
private val userDao: UserDao
) {
suspend fun insertUserData(userModel: UserModel) =
withContext(Dispatchers.IO) {
userDao.insertUserData(userModel)
}
fun checkUserLoginDetails(useremail: String, userpassword: String): Flow<Int> =
userDao.checkUserLoginDetails(useremail, userpassword)
}
Step 12:
Create new class UserViewModel.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.uiviewmodel
import `in`.kotlinkatta.kotlinkattademo.datastore.UserDataStore
import `in`.kotlinkatta.kotlinkattademo.model.UserModel
import `in`.kotlinkatta.kotlinkattademo.repository.UserRepository
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class UserViewModel
@Inject
constructor(
private val userRepository: UserRepository, private val userDataStore: UserDataStore
) : ViewModel() {
fun insertUserData(userModel: UserModel) = viewModelScope.launch {
userRepository.insertUserData(userModel)
}
fun checkUserLoginDetails(useremail: String, userpassword: String): Flow<Int> =
userRepository.checkUserLoginDetails(useremail, userpassword).flowOn(Dispatchers.Main)
fun saveToLocal_userloginstatus(userloginstatus: String) =
viewModelScope.launch(Dispatchers.IO) {
userDataStore.saveToLocal_userloginstatus(userloginstatus)
}
val readFromLocal_userloginstatus = userDataStore.readFromLocal_userloginstatus
}
Step 13:
Open Activity MainActivity.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo
import `in`.kotlinkatta.kotlinkattademo.databinding.ActivityMainBinding
import `in`.kotlinkatta.kotlinkattademo.uiviewmodel.UserViewModel
import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collect
@AndroidEntryPoint
public class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val userViewModel: UserViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
funCheckUserLoginOrNot()
}
private fun funCheckUserLoginOrNot() {
lifecycleScope.launchWhenStarted {
userViewModel.readFromLocal_userloginstatus.collect { userloginstatus ->
if (userloginstatus == "1") {
val intentActivity = Intent(applicationContext, WelcomeActivity::class.java)
intentActivity.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intentActivity)
finish()
}else {
val intentActivity = Intent(applicationContext, LoginActivity::class.java)
intentActivity.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intentActivity)
finish()
}
}
}
}
}
Step 14:
Open layout xml file activity_main.xml and add following code.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="15dp"
android:background="#9999"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="KotlinKatta"
android:textSize="35dp"
android:fontFamily="sans-serif-black"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Step 15:
Create new Activity LoginActivity.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo
import `in`.kotlinkatta.kotlinkattademo.databinding.ActivityLoginBinding
import `in`.kotlinkatta.kotlinkattademo.uiviewmodel.UserViewModel
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
@AndroidEntryPoint
class LoginActivity : AppCompatActivity() {
private lateinit var binding: ActivityLoginBinding
private val userViewModel: UserViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLoginBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
funinit()
}
private fun funinit() {
binding.apply {
btnlogin.setOnClickListener {
val strEmailId = edtemail.text.toString()
val strPassword = edtpassword.text.toString()
if (strEmailId.equals("") || strPassword.equals("")) {
Toast.makeText(applicationContext, "Please Enter Details.", Toast.LENGTH_SHORT)
.show()
} else {
lifecycleScope.launch {
userViewModel.checkUserLoginDetails(strEmailId, strPassword)
.collect { values ->
val user_exists = values
if (user_exists == 1) {
userViewModel.saveToLocal_userloginstatus("1")
val intent =
Intent(applicationContext, WelcomeActivity::class.java)
startActivity(intent)
finish()
} else {
Toast.makeText(
applicationContext,
"Enter Valid Email Id & Password.",
Toast.LENGTH_SHORT
)
.show()
}
}
}
}
}
binding.btnsignup.setOnClickListener {
val intent = Intent(applicationContext, SignUpActivity::class.java)
startActivity(intent)
finish()
}
}
}
}
Step 16:
Open layout xml file activity_login.xml and add following code.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#9999"
tools:context=".LoginActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Hello World!"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-black"
android:textColor="#000000"
android:textSize="30dp"
android:text="Login"
android:gravity="center"
android:layout_marginTop="10dp"
android:layout_marginBottom="30dp"/>
<EditText
android:id="@+id/edtemail"
android:hint="Enter Email Id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_margin="5dp"
android:background="#E7E7E7"/>
<EditText
android:id="@+id/edtpassword"
android:hint="Enter Password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_margin="5dp"
android:background="#E7E7E7"/>
<androidx.appcompat.widget.AppCompatButton
android:layout_marginTop="10dp"
android:id="@+id/btnlogin"
android:text="Login"
android:textAllCaps="false"
android:background="#FF03DAC5"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.appcompat.widget.AppCompatButton
android:layout_marginTop="10dp"
android:id="@+id/btnsignup"
android:textAllCaps="false"
android:text="Create New Account"
android:background="#FF03DAC5"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Step 17:
Create new Activity SignUpActivity.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo
import `in`.kotlinkatta.kotlinkattademo.databinding.ActivitySignUpBinding
import `in`.kotlinkatta.kotlinkattademo.model.UserModel
import `in`.kotlinkatta.kotlinkattademo.uiviewmodel.UserViewModel
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class SignUpActivity : AppCompatActivity() {
private lateinit var binding: ActivitySignUpBinding
private val userViewModel: UserViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySignUpBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
funinit()
}
private fun funinit() {
binding.apply {
btnsubmit.setOnClickListener {
val strEmailId = edtemailid.text.toString()
val strPassword = edtpassword.text.toString()
if (strEmailId.equals("") || strPassword.equals("")) {
Toast.makeText(applicationContext, "Please Enter Details.", Toast.LENGTH_SHORT)
.show()
} else {
userViewModel.insertUserData(UserModel(strEmailId, strPassword))
val intent = Intent(applicationContext, MainActivity::class.java)
startActivity(intent)
finish()
}
}
}
}
}
Step 18:
open layout xml file activity_sign_up.xml and add following code.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SignUpActivity"
android:background="#9999">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Hello World!"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-black"
android:textColor="#000000"
android:textSize="30dp"
android:text="Sign Up"
android:gravity="center"
android:layout_marginTop="10dp"
android:layout_marginBottom="30dp"/>
<EditText
android:id="@+id/edtemailid"
android:hint="Enter Email Id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_margin="5dp"
android:background="#E7E7E7"/>
<EditText
android:id="@+id/edtpassword"
android:hint="Enter Password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_margin="5dp"
android:background="#E7E7E7"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnsubmit"
android:layout_margin="5dp"
android:text="Submit"
android:textAllCaps="false"
android:background="#FF03DAC5"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Step 19:
Create new Activity WelcomeActivity.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo
import `in`.kotlinkatta.kotlinkattademo.databinding.ActivityWelcomeBinding
import `in`.kotlinkatta.kotlinkattademo.uiviewmodel.UserViewModel
import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class WelcomeActivity : AppCompatActivity() {
private lateinit var binding: ActivityWelcomeBinding
private val userViewModel: UserViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityWelcomeBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
funinit()
}
private fun funinit() {
binding.apply {
btnlogout.setOnClickListener {
userViewModel.saveToLocal_userloginstatus("0")
val intent = Intent(applicationContext, MainActivity::class.java)
startActivity(intent)
finish()
}
}
}
}
Step 20:
Open layout xml file activity_welcome.xml and add following code.project
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="15dp"
android:background="#9999"
tools:context=".WelcomeActivity">
<TextView
android:layout_marginTop="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:fontFamily="sans-serif-black"
android:textColor="#000000"
android:textSize="30dp"
android:text="Welcome To DashBoard"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatButton
android:layout_margin="10dp"
android:id="@+id/btnlogout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FF03DAC5"
android:text="Logout"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Comments
Post a Comment